package org.zjx.route;

import com.alibaba.fastjson2.JSON;
import org.zjx.common.bean.Npc;
import org.zjx.common.bean.Screen;
import org.zjx.common.bean.Transfer;
import org.zjx.common.entity.Address;
import org.zjx.common.route.*;
import org.zjx.db.NpcDB;
import org.zjx.db.ScreenDB;
import org.zjx.db.TransferDB;

import java.awt.*;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

public class RouteUtil {

    /**
     * 飞行符路径
     */
    public static List<IRoute> flyingCharms(Address startAddress) {
        List<IRoute> flyingCharmsRoutes = new ArrayList<>();
        flyingCharmsRoutes.add(new FlyingCharmsRoute(startAddress, FlyingCharmsRoute.FLY_CITY_CSC));
        flyingCharmsRoutes.add(new FlyingCharmsRoute(startAddress, FlyingCharmsRoute.FLY_CITY_XLNG));
        flyingCharmsRoutes.add(new FlyingCharmsRoute(startAddress, FlyingCharmsRoute.FLY_CITY_BXG));
        flyingCharmsRoutes.add(new FlyingCharmsRoute(startAddress, FlyingCharmsRoute.FLY_CITY_ZZG));
        flyingCharmsRoutes.add(new FlyingCharmsRoute(startAddress, FlyingCharmsRoute.FLY_CITY_CAC));
        flyingCharmsRoutes.add(new FlyingCharmsRoute(startAddress, FlyingCharmsRoute.FLY_CITY_JYC));
        flyingCharmsRoutes.add(new FlyingCharmsRoute(startAddress, FlyingCharmsRoute.FLY_CITY_ALG));
        return flyingCharmsRoutes;
    }

    /**
     * 飞行旗路径
     * @param flyingFlagsPoints  多个飞行旗及其点位集合
     */
    public static List<IRoute> flyingFlags(Address startAddress,List<FlyingFlagsRoute.FlyingFlagsPoint> flyingFlagsPoints) {
        List<IRoute> flyingFlagsRoutes = new ArrayList<>();
        if (flyingFlagsPoints == null) return flyingFlagsRoutes;
        for (FlyingFlagsRoute.FlyingFlagsPoint flyingFlagsPoint : flyingFlagsPoints) {
            String cityName = flyingFlagsPoint.getCityName();
            for (Point point : flyingFlagsPoint.getPoints()) {
                flyingFlagsRoutes.add(new FlyingFlagsRoute(startAddress, new Address(cityName, point.x, point.y)));
            }
        }
        return flyingFlagsRoutes;
    }

    /**
     * 传送点位
     */
    public static List<IRoute> wayPoint(){
        List<Transfer> transfers = new TransferDB().findAll();
        List<IRoute> transferRoutes = new ArrayList<>();
        for (Transfer transfer : transfers) {
            Screen startScreen = new ScreenDB().findById(transfer.getScreenId());
            Screen endScreen = new ScreenDB().findById(transfer.getTargetScreenId());
            Point startPoint = JSON.parseObject(transfer.getLocation(), Point.class);
            Point endPoint = JSON.parseObject(transfer.getTargetLocation(), Point.class);
            Point clickPoint = JSON.parseObject(transfer.getClickPoint(), Point.class);

            Address sAddress = new Address(startScreen.getName(),startPoint.x,startPoint.y);
            Address eAddress = new Address(endScreen.getName(), endPoint.x,endPoint.y);
            if (transfer.getType() == 0) {
                TransferApertureRoute route = new TransferApertureRoute(sAddress,eAddress);
                route.setClickPoint(clickPoint);
                transferRoutes.add(route);
            }else if (transfer.getType() == 1) {
                TransferApertureRoute route = new TransferApertureRoute(sAddress,eAddress);
                route.setToRoom(true);
                route.setClickPoint(clickPoint);
                transferRoutes.add(route);
            }else if (transfer.getType() == 3) {
                TransferNPCRoute route = new TransferNPCRoute(sAddress,eAddress);
                Npc npc = new NpcDB().findById(transfer.getNpcId());
                route.setNpcName(npc.getName());
                String ss = transfer.getDialogues();
                if (ss != null && !ss.isEmpty()){
                    List<String> dialogues = JSON.parseArray(ss, String.class);
                    route.setDialogues(dialogues);
                }
                route.setNpcAddress(new Address(startScreen.getName(), clickPoint.x, clickPoint.y));
                transferRoutes.add(route);
            }
        }
        return transferRoutes;
    }

    /**
     * 连接两个路线集合：用start集合的终点与end集合的起点在同一城市的路线生成新步行路线
     * @param start 起点路线集合（新路线的起点来自这些路线的终点）
     * @param end 终点路线集合（新路线的终点来自这些路线的起点）
     * @return 生成的所有新路线集合（排除自连接和坐标相同的情况）
     */
    public static List<IRoute> connectRoutesWithinSameCity(List<IRoute> start, List<IRoute> end) {
        List<IRoute> result = new ArrayList<>();

        // 按起点城市对end路线分组，优化查找效率
        Map<String, List<IRoute>> endRoutesByStartCity = end.stream()
                .collect(Collectors.groupingBy(route -> route.getStart().getCity()));

        for (IRoute currentRoute : start) {
            Address currentEnd = currentRoute.getEnd();
            List<IRoute> possibleNextRoutes = endRoutesByStartCity.get(currentEnd.getCity());

            if (possibleNextRoutes != null) {
                for (IRoute nextRoute : possibleNextRoutes) {
                    // 跳过自连接（同一个Route实例）
                    if (currentRoute == nextRoute) continue;

                    // 跳过坐标相同的连接
                    if (currentEnd.getX() == nextRoute.getStart().getX() &&
                            currentEnd.getY() == nextRoute.getStart().getY()) {
                        continue;
                    }

                    RunRoute runRoute = new RunRoute(currentEnd, nextRoute.getStart());
                    if (currentRoute instanceof TransferApertureRoute) {
                        if (((TransferApertureRoute) currentRoute).isToRoom()) runRoute.setRoom(true);
                    }else if (currentRoute instanceof RunRoute) {
                        if (runRoute.isRoom()) runRoute.setRoom(true);
                    }

                    // 创建新步行路线
                    result.add(runRoute);
                }
            }
        }
        return result;
    }



    /**
     * 更精确的去重方法（基于起点和终点的坐标+城市）
     */
    public static List<IRoute> deduplicateRoutes(List<IRoute> routes) {
        Map<String, IRoute> uniqueRoutes = new LinkedHashMap<>();
        for (IRoute route : routes) {
            String key = buildRouteKey(route);
            uniqueRoutes.putIfAbsent(key, route);
        }

        return new ArrayList<>(uniqueRoutes.values());
    }

    private static String buildRouteKey(IRoute route) {
        return String.format("%s|%d|%d|%s|%d|%d",
                route.getStart().getCity(),
                route.getStart().getX(),
                route.getStart().getY(),
                route.getEnd().getCity(),
                route.getEnd().getX(),
                route.getEnd().getY()
        );
    }




    public static List<IRoute> getStartEndRoutes(Address startAddress,Address endAddress,List<IRoute> routes) {
        List<IRoute> startRoutes = routes.stream()
                .filter(route ->
                        route.getStart().getCity().equals(startAddress.getCity())
                )
                .collect(Collectors.toList());
        List<IRoute> endRoutes = routes.stream()
                .filter(route ->
                        route.getEnd().getCity().equals(endAddress.getCity())
                )
                .collect(Collectors.toList());

        return getIRoutes(startRoutes, endRoutes,startAddress,endAddress);
    }

    private static List<IRoute> getIRoutes(List<IRoute> startRoutes, List<IRoute> endRoutes,Address startAddress,Address endAddress) {
        List<IRoute> result = new ArrayList<>();
        for (IRoute route : startRoutes) {
            if (route instanceof FlyingCharmsRoute || route instanceof FlyingFlagsRoute) continue;
            if (startAddress.equals(route.getStart())) continue;
            RunRoute runRoute = new RunRoute(startAddress, route.getStart());
            result.add(runRoute);
        }

        for (IRoute route : endRoutes) {
            if (route.getEnd().equals(endAddress)) continue;
            RunRoute runRoute = new RunRoute(route.getEnd(), endAddress);
            if (route instanceof TransferApertureRoute){
                if (((TransferApertureRoute) route).isToRoom()) runRoute.setRoom(true);
            }else if (route instanceof RunRoute){
                if (runRoute.isRoom()) runRoute.setRoom(true);
            }
            result.add(runRoute);
        }

        if (startAddress.getCity().equals(endAddress.getCity())) {
            RunRoute runRoute = new RunRoute(startAddress, endAddress);
            result.add(runRoute);
        }
        return result;
    }
}
